#pragma once
#include <iostream>
#include <vector>
#include <atomic>
#include <mutex>




template <typename T>
class MPMCBoundedQueue
{
    static_assert(
        std::is_move_constructible<T>::value, "Should be of movable type");

public:
    /**
     * @brief MPMCBoundedQueue Constructor.
     * @param size Power of 2 number - queue length.
     * @throws std::invalid_argument if size is bad.
     */
    explicit MPMCBoundedQueue(size_t size)
        : m_buffer(size), m_buffer_mask(size - 1), m_enqueue_pos(0),
          m_dequeue_pos(0)
    {
        bool size_is_power_of_2 = (size >= 2) && ((size & (size - 1)) == 0);
        if(!size_is_power_of_2)
        {
            throw std::invalid_argument("buffer size should be a power of 2");
        }

        for(size_t i = 0; i < size; ++i)
        {
            m_buffer[i].sequence = i;
        }
    }

    /**
     * @brief Move ctor implementation.
     */
    MPMCBoundedQueue(MPMCBoundedQueue&& rhs) noexcept
    {
        *this = std::move(rhs);
    }

    /**
     * @brief Move assignment implementaion.
     */
    MPMCBoundedQueue& operator=(MPMCBoundedQueue&& rhs) noexcept
    {
        if (this != &rhs)
        {
            m_buffer = std::move(rhs.m_buffer);
            m_buffer_mask = std::move(rhs.m_buffer_mask);
            m_enqueue_pos = rhs.m_enqueue_pos.load();
            m_dequeue_pos = rhs.m_dequeue_pos.load();
        }
        return *this;
    }

    /**
     * @brief push Push data to queue.
     * @param data Data to be pushed.
     * @return true on success.
     */
    template <typename U>
    bool push(U&& data)
    {
        Cell* cell;
        size_t pos = m_enqueue_pos.load(std::memory_order_relaxed);
        for(;;)
        {
            cell = &m_buffer[pos & m_buffer_mask];
            size_t seq = cell->sequence.load(std::memory_order_acquire);
            intptr_t dif = (intptr_t)seq - (intptr_t)pos;
            if(dif == 0)
            {
                if(m_enqueue_pos.compare_exchange_weak(
                       pos, pos + 1, std::memory_order_relaxed))
                {
                    break;
                }
            }
            else if(dif < 0)
            {
                return false;
            }
            else
            {
                pos = m_enqueue_pos.load(std::memory_order_relaxed);
            }
        }

        cell->data = std::forward<U>(data);

        cell->sequence.store(pos + 1, std::memory_order_release);

        return true;
    }

    /**
     * @brief pop Pop data from queue.
     * @param data Place to store popped data.
     * @return true on sucess.
     */
    bool pop(T& data)
    {
        Cell* cell;
        size_t pos = m_dequeue_pos.load(std::memory_order_relaxed);
        for(;;)
        {
            cell = &m_buffer[pos & m_buffer_mask];
            size_t seq = cell->sequence.load(std::memory_order_acquire);
            intptr_t dif = (intptr_t)seq - (intptr_t)(pos + 1);
            if(dif == 0)
            {
                if(m_dequeue_pos.compare_exchange_weak(
                       pos, pos + 1, std::memory_order_relaxed))
                {
                    break;
                }
            }
            else if(dif < 0)
            {
                return false;
            }
            else
            {
                pos = m_dequeue_pos.load(std::memory_order_relaxed);
            }
        }

        data = std::move(cell->data);

        cell->sequence.store(
            pos + m_buffer_mask + 1, std::memory_order_release);

        return true;
    }

private:
    struct Cell
    {
        std::atomic<size_t> sequence;
        T data;

        Cell() = default;

        Cell(const Cell&) = delete;
        Cell& operator=(const Cell&) = delete;

        Cell(Cell&& rhs)
            : sequence(rhs.sequence.load()), data(std::move(rhs.data))
        {
        }

        Cell& operator=(Cell&& rhs)
        {
            sequence = rhs.sequence.load();
            data = std::move(rhs.data);

            return *this;
        }
    };

private:
    typedef char Cacheline[64];

    Cacheline pad0;
    std::vector<Cell> m_buffer;
    /* const */ size_t m_buffer_mask;
    Cacheline pad1;
    std::atomic<size_t> m_enqueue_pos;
    Cacheline pad2;
    std::atomic<size_t> m_dequeue_pos;
    Cacheline pad3;
};






// template <typename T>
// class RingQueue
// {
// 	static_assert(std::is_move_constructible<T>::value,
// 				  "Should be of movable type");

// public:
// 	RingQueue(size_t size)
// 		: _queue(size), _en_queue(0), _de_queue(0)
// 	{
// 		bool size_is_power_of_2 = (size >= 2) && ((size & (size - 1)) == 0);
// 		if (!size_is_power_of_2)
// 		{
// 			throw std::invalid_argument("buffer size should be a power of 2");
// 		}
// 		mask = size - 1;
// 	}

// 	RingQueue(RingQueue &&q) noexcept
// 	{
// 		*this = std::move(q);
// 	}
// 	RingQueue &operator=(RingQueue &&q) noexcept
// 	{
// 		if (this != &q)
// 		{
// 			_queue = std::move(q._queue);
// 			mask = std::move(q.mask);
// 			_en_queue = q._en_queue.load();
// 			_de_queue = q._de_queue.load();
// 		}
// 		return *this;
// 	}

// 	template <typename U>
// 	bool push(U &&val)
// 	{

// 		size_t pos;
// 		while (true)
// 		{
// 			pos = _en_queue.load(std::memory_order_relaxed);

// 			if (((pos + 1) & mask) == _de_queue.load(std::memory_order_acquire))
// 			{
// 				// std::cout << "线程" << "任务失败" << std::endl;
// 				return false;
// 			}

// 			if (_en_queue.compare_exchange_weak(pos, (pos + 1) & (mask), std::memory_order_release))
// 			{
// 				break;
// 			}
// 		}

// 		// std::cout << "线程" << "任务" << std::endl;
// 		_queue[pos] = std::forward<U>(val);
// 		return true;
// 	}

// 	bool pop(T &data)
// 	{
// 		size_t pos;
// 		// std::cout << "线程" << "弹出任务" << std::endl;
// 		while (true)
// 		{
// 			pos = _de_queue.load(std::memory_order_relaxed);

// 			if (pos == _en_queue.load(std::memory_order_acquire))
// 			{
// 				// std::cout << "线程" << "弹出任务失败" << std::endl;
// 				return false;
// 			}

// 			if (_de_queue.compare_exchange_weak(pos, (pos + 1) & (mask), std::memory_order_release))
// 			{
// 				break;
// 			}
// 		}

// 		data = std::move(_queue[pos]);
// 		return true;
// 	}

// 	~RingQueue()
// 	{
// 	}

// private:
// 	RingQueue(RingQueue &) = delete;
// 	RingQueue &operator=(RingQueue &) = delete;

// 	typedef char cacheline[64];

// 	size_t mask;
// 	cacheline pad1;
// 	std::vector<T> _queue;
// 	cacheline pad2;
// 	std::atomic<size_t> _en_queue;
// 	cacheline pad3;
// 	std::atomic<size_t> _de_queue;
// };
